#include "fraction.h"


#pragma region Metody publiczne 

Fraction::Fraction()
	: m_Integer(0)
	, m_Numerator(0)
	, m_Denominator(0)
	, m_IsZero(true)
	, m_Sign(true)
{
}

Fraction::Fraction(quint32 cnt, quint32 num, quint32 den, bool plus)
	: m_Integer(cnt)
	, m_Sign(plus)
{	
	if(den != 0)
	{
		int nwd = NWD(num, den);
		
		m_Numerator = num/nwd;
		m_Denominator = den/nwd;
	}

	if(m_Numerator >= m_Denominator)
	{
		int plus = (int)(m_Numerator / m_Denominator);
		m_Integer = cnt + plus;
		m_Numerator -= plus * m_Denominator; 
	}
	else
		m_Integer = cnt;

	if(cnt == 0 && num == 0) m_IsZero = true;
	else m_IsZero = false;
}

Fraction::Fraction(QString asd)
{
	m_Sign = true;
	m_Integer = 0;
	m_Numerator = 0;
	m_Denominator = 0;
	if(asd.contains('-'))
	{
		m_Sign = false;
		asd = asd.split('-', QString::SkipEmptyParts).at(0);
	}
	if(asd.contains('i'))
	{
		QStringList tmp = asd.split('i');
		m_Integer = tmp[0].toInt();		
		tmp = tmp[1].split('|');
		m_Numerator = tmp[0].toInt();
		m_Denominator = tmp[1].toInt();		
	}
	else if(asd.contains('|'))
	{
		QStringList tmp = asd.split('|');
		m_Integer = 0;

		m_Numerator = tmp[0].toInt();
		m_Denominator = tmp[1].toInt();	
	}
	else
		m_Integer = asd.toInt();	

	normalize();
}

Fraction::~Fraction()
{
}

bool Fraction::isZero()
{
	return m_IsZero;
}

QString Fraction::toString()
{
	QString ret;

	if(m_Sign == false && m_IsZero != true)
		ret.append(QObject::tr("-"));
	if(m_Integer != 0)
		ret.append(QString::number(m_Integer));			
	if(m_Integer != 0 && m_Numerator != 0)
		ret.append(QObject::tr("i"));
	if(m_Numerator != 0)
	{
		ret.append(QString::number(m_Numerator));
		ret.append(QObject::tr("|"));
		ret.append(QString::number(m_Denominator));
	}
	if(m_IsZero == true)
		ret.append(QString::number(0));

	
	return ret;
}

void Fraction::changeSign()
{
	m_Sign = !m_Sign;
}

void Fraction::normalize()
{
	if(m_Numerator == 0)
		m_Denominator = 0;

	if(m_Denominator != 0)
	{
		int nwd = NWD(m_Numerator, m_Denominator);
	
		m_Numerator = m_Numerator/nwd;
		m_Denominator = m_Denominator/nwd;	

		if(m_Numerator >= m_Denominator)
		{
			int plus = (int)(m_Numerator / m_Denominator);
			m_Integer += plus;
			m_Numerator -= plus * m_Denominator; 
		}	
	}

	if(m_Integer == 0 && m_Numerator == 0) m_IsZero = true;
	else m_IsZero = false;
}

#pragma endregion

#pragma region operatory

std::istream& operator>>(std::istream &in, Fraction& f)
{
	std::string tmp;

	in >> tmp;

	QString asd = QString::fromStdString(tmp);

	if(asd.contains('-'))
	{
		f.m_Sign = false;
	}
	if(asd.contains('i'))
	{
		QStringList tmp = asd.split('i');
		f.m_Integer = tmp[0].toInt();		
		tmp = tmp[1].split('|');
		f.m_Numerator = tmp[0].toInt();
		f.m_Denominator = tmp[1].toInt();		
	}
	else if(asd.contains('|'))
	{
		QStringList tmp = asd.split('|');
		f.m_Integer = 0;

		f.m_Numerator = tmp[0].toInt();
		f.m_Denominator = tmp[1].toInt();	
	}
	else
		f.m_Integer = asd.toInt();	

	f.normalize();

	return in;
}

std::ostream& operator<<(std::ostream &o, Fraction& f)
{
	if(f.m_Sign == false && f.m_IsZero != true)
		o << "-";
	if(f.m_Integer != 0)
		o << f.m_Integer;
	if(f.m_Integer != 0 && f.m_Numerator != 0)
		o << "i";
	if(f.m_Numerator != 0)
		o << f.m_Numerator << "|" << f.m_Denominator;
	if(f.m_IsZero == true)
		o << 0;

	return o;
}

bool Fraction::operator ==(const Fraction &f) const
{
	return (m_Integer == f.m_Integer && m_Numerator == f.m_Numerator && m_Denominator == f.m_Denominator && m_Sign == f.m_Sign) || (m_IsZero == f.m_IsZero && m_IsZero == true);
}

bool Fraction::operator !=(const Fraction &f) const
{
	return !(*this == f);
}

bool Fraction::operator >=(const Fraction &f) const
{
	return *this == f || *this > f;
}

bool Fraction::operator <=(const Fraction &f) const
{
	return *this == f || *this < f;
}

bool Fraction::operator >(const Fraction &f) const
{
	// rozne znaki
	if(m_Sign == true && f.m_Sign == false)
		return true;
	if(m_Sign == false && f.m_Sign == true)
		return false;

	// te same znaki
	if(m_Integer > f.m_Integer)		
		if(m_Sign == true)
			return true;
		else
			return false;
	else if(m_Integer < f.m_Integer)
		if(m_Sign == true)
			return false;
		else
			return true;		
	else
		if(m_Numerator * f.m_Denominator > m_Denominator * f.m_Numerator)
			if(m_Sign == true)
				return true;
			else
				return false;			
		else
			if(m_Sign == true)
				return false;
			else
				return true;			
}

bool Fraction::operator <(const Fraction &f) const
{
	bool b1 = (!(*this > f));
	bool b2 = (*this != f);
	return b1 && b2;
}

Fraction& Fraction::operator +=(const Fraction &f)// const
{
	*this = *this + f;
	
	return *this;
}

Fraction& Fraction::operator -=(const Fraction &f)// const
{
	*this = *this - f;
	
	return *this;
}

Fraction& Fraction::operator *=(const Fraction &f)// const
{
	*this = *this * f;
	
	return *this;
}

Fraction& Fraction::operator /=(const Fraction &f)// const
{
	*this = *this / f;
	
	return *this;
}

Fraction& Fraction::operator *(const Fraction &f)// const
{
	static Fraction ret;

	ret.m_Denominator = 0;
	ret.m_Integer = 0;
	ret.m_IsZero = true;
	ret.m_Numerator = 0;
	ret.m_Sign = true;

	if(m_Denominator != 0 && f.m_Denominator !=  0)
	{
		ret.m_Numerator = ((m_Integer * (quint64)m_Denominator) + m_Numerator) * ((f.m_Integer * (quint64)f.m_Denominator) + f.m_Numerator);
		ret.m_Denominator = m_Denominator * f.m_Denominator; 	
	}
	else
		if(m_Denominator == 0)
		{
			if(f.m_Denominator != 0)
			{
				ret.m_Numerator = (m_Integer) * ((f.m_Integer * (quint64)f.m_Denominator) + f.m_Numerator);
				ret.m_Denominator = f.m_Denominator;								
			}
			else
			{
				ret.m_Integer = m_Integer * f.m_Integer;
				ret.m_Denominator = 0;
				ret.m_Numerator = 0;
			}
				
		}
		else
		{		
			ret.m_Numerator = ((m_Integer * (quint64)m_Denominator) + m_Numerator) * (f.m_Integer);
			ret.m_Denominator = m_Denominator; 	
		}

	ret.normalize();

	ret.m_Sign  = !(m_Sign ^ f.m_Sign);

	return ret;
}

Fraction& Fraction::operator /(const Fraction &f)// const
{
	static Fraction ret;

	ret.m_Denominator = 0;
	ret.m_Integer = 0;
	ret.m_IsZero = true;
	ret.m_Numerator = 0;
	ret.m_Sign = true;

	Fraction f1 = *this;
	Fraction f2 = f;

	if(f1.m_Denominator == 0)
	{
		f1.m_Integer -= 1;
		f1.m_Numerator = 1;
		f1.m_Denominator = 1;
	}
	
	if(f2.m_Denominator == 0)
	{
		f2.m_Integer -= 1;
		f2.m_Numerator = 1;
		f2.m_Denominator = 1;
	}


	if(f2.m_IsZero) 
	{
		qDebug() << "ERR: Dzielisz przez zero!";
		return ret;
	}
	else
	{
		ret.m_Numerator = ((f1.m_Integer * (quint64)f1.m_Denominator) + f1.m_Numerator) * f2.m_Denominator ;
		ret.m_Denominator = f1.m_Denominator * ((f2.m_Integer * (quint64)f2.m_Denominator) + f2.m_Numerator); 	

		ret.normalize();

		ret.m_Sign  = !(f1.m_Sign ^ f2.m_Sign);

		return ret;
	}	
}

Fraction& Fraction::operator +(const Fraction &f)// const
{
	static Fraction ret;

	ret.m_Denominator = 0;
	ret.m_Integer = 0;
	ret.m_IsZero = true;
	ret.m_Numerator = 0;
	ret.m_Sign = true;

	Fraction f1 = *this;
	Fraction f2 = f;

	if(f1.m_Denominator == 0)
	{
		f1.m_Integer -= 1;
		f1.m_Numerator = 1;
		f1.m_Denominator = 1;
	}
	
	if(f2.m_Denominator == 0)
	{
		f2.m_Integer -= 1;
		f2.m_Numerator = 1;
		f2.m_Denominator = 1;
	}
	quint64 nww = NWW(f1.m_Denominator, f2.m_Denominator);

	if(f1.m_Sign == f2.m_Sign)	 // gdy znaki sa takie same
	{
		ret.m_Integer = f1.m_Integer + f2.m_Integer;
		ret.m_Sign = f1.m_Sign;
		
		
		ret.m_Numerator = (f1.m_Numerator * (nww/f1.m_Denominator)) + (f2.m_Numerator * (nww/f2.m_Denominator));
		ret.m_Denominator = nww;

		ret.normalize();
		
		return ret;		
	}
	else
	{	
		if(f1.m_Sign == true) // dodatnia plus ujemna
		{
			Fraction b = f2;
			b.m_Sign = true;

			if(*this > b) // dodatnia wieksza od ujemnej
			{
				ret.m_Sign = true;

				if(f1.m_Numerator*f2.m_Denominator >= f2.m_Numerator * f1.m_Denominator) // czesc ulamkowa pierwszego wieksza od drugiego
				{
					ret.m_Numerator = (f1.m_Numerator * (nww/f1.m_Denominator)) - (f2.m_Numerator * (nww/f2.m_Denominator));
					ret.m_Denominator = nww;					
					ret.m_Integer = f1.m_Integer - f2.m_Integer;
				}
				else
				{
					ret.m_Numerator = nww + (f1.m_Numerator * (nww/f1.m_Denominator)) - (f2.m_Numerator * (nww/f2.m_Denominator));
					ret.m_Denominator = nww;
					ret.m_Integer = f1.m_Integer - f2.m_Integer - 1;
				}
			}
			else // ujemna wieksza
			{
				ret.m_Sign = false;

				if(f2.m_Numerator*f1.m_Denominator >= f1.m_Numerator * f2.m_Denominator) // czesc ulamkowa pierwszego wieksza od drugiego
				{
					ret.m_Numerator = (f2.m_Numerator * (nww/f2.m_Denominator)) - (f1.m_Numerator * (nww/f1.m_Denominator));
					ret.m_Denominator = nww;					
					ret.m_Integer = f2.m_Integer - f1.m_Integer;
				}
				else
				{
					ret.m_Numerator = nww + (f2.m_Numerator * (nww/f2.m_Denominator)) - (f1.m_Numerator * (nww/f1.m_Denominator));
					ret.m_Denominator = nww;
					ret.m_Integer = f2.m_Integer - f1.m_Integer - 1;
				}
			}
		}
		else
			ret = f2 + f1;

		ret.normalize();
		
		return ret;				
	}

	ret.m_Numerator = (f1.m_Numerator * (nww/f2.m_Denominator)) + (f2.m_Numerator * (nww/f1.m_Denominator));
	ret.m_Denominator = nww;

	ret.normalize();

	return ret;
}

Fraction& Fraction::operator -(const Fraction &f)// const
{
	static Fraction ret;

	Fraction b = f;
	b.m_Sign = !f.m_Sign;

	ret = *this + b;
	return ret;
}

#pragma endregion

#pragma region Metody prywatne (NWW i NWD)

quint64 Fraction::NWD(quint64 a, quint64 b)
{
	while (a!=b) {
		if (a>b)
			a-=b; 
		else
			b-=a; 
	}
	return a;
}

quint64 Fraction::NWW(quint64 a, quint64 b)
{
	return (a*b)/NWD(a,b);
}

#pragma endregion